package md.system;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.hibernate.annotations.Check;

import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

//实际AuthorityName才是配置点，Authority表却是还没能CUD生成修改的记录。
/**初始化必备：用户权限角色。
 * Authority 目前只能手动建立， 系统重新搭建的初始化步骤之一：
 *ID类型：Long转为UUID后，关联id都得一起改：报错unsupported comparison operator: <int> = <uuid>
 *REST graphQL 都是要用@PreAuthorize("hasAnyRole('Master')")注解到具体的@Controller类和控制器接口方法上面的。REST还可以直接集中在org/fjsei/yewu/config/SecurityConfig.java进行授权；
 *graphQL内省关联对象的授权？：需要过滤器还是 @PostAuthorize  @PostFilter，#依赖代码嵌入Context中屏蔽非授权允许的内省字段报错？。
 * graphQL内省必须在读写接口源头就得限制敏感字段内省权限，或者干脆对于只读事务的可手动setXxx(null)；非只读事务的可用interface限制敏感字段,#需要报错还是返回null。
 * */
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "AUTHORITY" )
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Fast")
public class Authority {
    //改成UUID+ EnumType.STRING以后 就不要求顺序递增id;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    /**对接外部SSO的角色名称。
     * 直接上字符串enum保险点,用数字还要再映射,可惜用不上中文
     * 数据库直接输入 字符串 EnumType.STRING。 Id配对=系统保障id:name映射;
     *2025/06/05 Authority 省略前缀"ROLE_" +的
     * */
    @Column(length =40, unique = true, nullable = false)
    @Enumerated(EnumType.STRING)
    @Check(constraints = "1=1")  // 覆盖默认的 CHECK 约束: 避免表里面旧名字不在枚举里面立马会报错的
    private AuthorityName_Enum name;


    /** 注解的角色描述
     * desc不能用是SQL关键字。 只会 WARN 建表不成功
     * */
    private String  title;


    //设置小心，故障：hibernate.LazyInitializationException: failed to lazily initialize
    //FetchType.LAZY，产生查询异常失败；有意外好处，切断graphQL的关联查询嵌套，避免信息安全问题。
    @ManyToMany(mappedBy = "authorities")
    private List<User> users= new LinkedList<>();
    //private Set<Isp> isp= new HashSet<>();

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    public AuthorityName_Enum getName() {
        return name;
    }

    public void setName(AuthorityName_Enum name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

/* 文档说明   https://blog.csdn.net/geejkse_seff/article/details/126504959
Spring Security——基于表达式的权限控制，Spring 表达式语言(Spring EL)；SpEL 操作符; 正则表达式匹配?
    //@PreAuthorize("hasRole('ROLE_'.concat(this.class.simpleName))")
    //SpEL怎样从List、Map集合中取值; @Value("#{numberBean.no == 999 and numberBean.no < 900}")
    // @PreAuthorize("hasRole('ROLE_USER') and hasIpAddress('localhost')" )
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    @PreAuthorize("#user.name.equals('david')")
    public void add(User user) {}
    @PreAuthorize("#id<100")
    public User find(int id) {}
只能在方法调用完成后检查权限决定是否要抛出AccessDeniedException。#数据库的事务要回滚的。
@PostAuthorize(“returnObject.id%2==0”)
public User find(int id) {}
 @PreFilter(filterTarget="ids", value="filterObject%2==0")
 public void delete(List<Integer> ids, List<String> usernames) {}集合类型的参数或List[]返回值进行过滤#?不会抛出AccessDeniedException

 【数据库脚本】  需要修改遗留的角色名字的；只能首先删除约束：
 ALTER TABLE public.authority DROP CONSTRAINT check_name;
* */
